/*
 * @Author: xiaosihan 
 * @Date: 2023-05-28 23:22:46 
 * @Last Modified by: xiaosihan
 * @Last Modified time: 2024-01-21 00:21:55
 */


//mesh
import { AnimationMixer, BufferGeometry, DoubleSide, Group, Mesh, MeshStandardMaterial, Object3D } from "three";
import threeLoader from "three-base/threeLoader";
import threeUtils from "three-base/threeUtils";


// 模型基类
export default class MeshBase extends Group {
    constructor(url?: string) {
        super();

        url && this.loadModal(url);
    }

    url = "";

    // 是否加载完成
    loaded = false;

    // 身体动作混合器
    mixer?: AnimationMixer;

    // 模型对象
    static modals: Map<string, Object3D> = new Map();

    // 模型是否在加载中
    static loadModaling: Map<string, boolean> = new Map();

    async loadModal(url: string) {

        this.clear();

        if (!url) {
            return;
        }

        this.url = url;

        this.loaded = false;

        // 没有模型并且没有加载任务时就去加载模型
        if (!MeshBase.modals.get(url) && !MeshBase.loadModaling.get(url)) {

            MeshBase.loadModaling.set(url, true);

            const gltfObj = await threeLoader.gltfLoader.loadAsync(url);

            threeUtils.center(gltfObj.scene);
            gltfObj.scene.updateMatrixWorld(true);

            // 应用全部变换
            gltfObj.scene.traverse(obj => {
                const mesh = obj as Mesh;
                mesh.position.set(0, 0, 0);
                mesh.rotation.set(0, 0, 0);
                mesh.scale.set(1, 1, 1);
                if (mesh.isMesh) {
                    mesh.geometry.applyMatrix4(mesh.matrixWorld);
                    const newMaterial = (mesh.material as MeshStandardMaterial).clone();
                    newMaterial.side = DoubleSide;
                    newMaterial.map && (newMaterial.map.anisotropy = threeLoader.anisotropy);
                    newMaterial.normalMap && (newMaterial.normalMap.anisotropy = threeLoader.anisotropy);
                    newMaterial.envMap && (newMaterial.envMap.anisotropy = threeLoader.anisotropy);
                    mesh.material = newMaterial;
                }
            });

            MeshBase.modals.set(url, gltfObj.scene);
            MeshBase.loadModaling.set(url, false);
        }

        // 如果模型还在加载中, 就等待模型加载完成
        do {
            await new Promise(resolve => setTimeout(resolve, 200));
        } while (MeshBase.loadModaling.get(url));

        if (MeshBase.modals.get(url)) {
            const mesh = MeshBase.modals.get(url)!.clone();
            this.clear();
            this.add(mesh);
        }

        await new Promise(resolve => requestAnimationFrame(resolve));

        // 加载完成事件
        this.dispatchEvent<any>({ type: "loaded" });

        this.loaded = true;

        return MeshBase.modals.get(url);
    }

    // 超出绘制区域时不绘制
    frustumCulled = false;

    // 创建阴影
    castShadow: boolean = false;

    // 接受阴影
    receiveShadow: boolean = false;


    updateMatrix(): void {

        // 深度应用属性
        this.traverse(obj => {
            const mesh = obj as Mesh;
            if (mesh.isMesh) {
                mesh.frustumCulled = this.frustumCulled;
                mesh.castShadow = this.castShadow;
                mesh.receiveShadow = this.receiveShadow;
            }
        });

        super.updateMatrix();
    }

    // 遍历模型
    traverseMesh(call: (mesh: Mesh<BufferGeometry, MeshStandardMaterial>) => void = () => { }) {
        this.traverse(obj => {
            const mesh = obj as Mesh<BufferGeometry, MeshStandardMaterial>;
            if (mesh.isMesh) {
                call(mesh);
            }
        });
    }

}